系统调用 open 在进程和终端之间创建一个连接, 现在来仔细了解一下与终端链接的一些 属性

终端的 I/O

  1. 进程在用户输入 Return 后才接受数据
  2. 进程将用户输入的 Return(ASCII 码 13) 看做换行符 (ASCII 码10)
  3. 进程发送换行符, 终端接受回车换行符

终端驱动程序

处理进程和外部设备间数据流的内核子程序的集合被称为终端驱动程序或 tty 驱动程序

终端驱动程序相关函数

该变终端驱动程序的设置就像改变磁盘文件连接的设置一样:

  1. 从驱动程序获得属性;
  2. 修改所要修改的属性;
  3. 将修改过的属性送回驱动程序.
    #include <termios.h>
    struct termios attribs;             /* struct to hold attributes */
    tcgetattr(fd, &attribs);            /* get attribs from driver */
    attribs.c_lflag |= ECHO;            /* turn on ECHO bit in flagset */
    tcsetattr(fd, TCSANOW, &attribs);   /* send attribs back to driver */
    
  tcgetattr
目标 读取 tty 驱动程序的属性
头文件 #include <termios.h> / #include <unistd.h>
原型 int result = tcgetattr(int fd, struct termios * info);
参数 fd 终端文件描述符 / info 指向终端结构的指针
返回值 -1 error 0 success

tcgetattr 从文件fd相关的驱动程序中获取当前设置, 并把它复制到 info 指针所指的 结构中

  tcsetattr
目的 设置 tty 驱动程序的属性
头文件 #include <termios.h> / #include <unistd.h>
原型 int result = tcsetattr(int fd, int when, struct termios *info);
参数 fd 终端文件描述符, when 改变设置的时间, info 指向终端结构的指针
返回值 -1 error , 0 success

when 的允许值如下:

  1. TCSANOW 立即更新驱动程序设置
  2. TCSADRAIN 等待知道驱动程序队列中所有输出都被传送到终端
  3. TCSAFLUSH 等待知道驱动程序队列中所有输出都被传送出去, 然后释放所有队列中的输入数据, 并进行一定变化

编写终端驱动程序: 关于位

termios 结构类型包括了若干个标志集和一个控制字符的数组, 所有 Unix 版本包含一下 结构

struct termios {
    tcflag_t    c_iflag;        /* input mode flags */
    tcflag_t    c_oflag;        /* output mode flags */
    tcflag_t    c_cflag;        /* control mode flags */
    tcflag_t    c_lflag;        /* local mode flags */
    cc_t        c_cc[NCCS];     /* control characters */
    speed_t     c_ispeed;       /* input speed */
    speed_t     c_ospeed;       /* output speed */
};

终端驱动程序: 几个例子

echostate.c ---- 显示回显位状态

#include <stdio.h>
#include <termios.h>

int main()
{
    struct termios info;
    int rv;

    rv = tcgetattr(0, &info);   /* read values from driver */
    if (rv == -1){
        perror("tcgetattr");
        return 1;
    }

    if (info.c_lflag & ECHO)
        printf("echo is on, since its bit is 1\n");
    else
        printf("echo is OFF, since its bit is 0\n");
}

setecho.c ---- 改变回显位的状态

#include <stdio.h>
#include <termios.h>

#define oops(s, x){perror(s); exit(x);}

    int
main( int argc, char **argv )
{
    struct termios info;
    if (argc == 1)
        return 0;
    if ( tcgetattr(0, &info) == -1 )
        oops("tcgetattr", 1);

    if (argv[1][0] == 'y')
        info.c_lflag |= ECHO;
    else
        info.c_lflag &= ~ECHO;
    if (tcsetattr(0, TCSANOW, &info) == -1)
        oops("tcsetattr", 2);
    return 0;
}

showtty.c ---- 显示大量驱动程序属性

#include <stdio.h>
#include <termios.h>

void showbaud(int);
void show_some_flags(struct termios *);

int main()
{
    struct termios ttyinfo;

    if ( tcgetattr(0, &ttyinfo)){
        perror("cannot get params about stdin");
        return 1;
    }

    showbaud(cfgetospeed(&ttyinfo));
    printf("The erase character is ascii %d, Ctrl-%c\n",
            ttyinfo.c_cc[VERASE], ttyinfo.c_cc[VERASE] - 1 + 'A');
    printf("The line kill character is ascii %d, Ctrl-%c\n",
            ttyinfo.c_cc[VKILL], ttyinfo.c_cc[VKILL] - 1 + 'A');
    show_some_flags(&ttyinfo);
}

void showbaud(int thespeed)
{
    printf("The baud rate is");
    switch(thespeed){
        case B300: printf("300\n"); break;
        case B600: printf("600\n"); break;
        case B1200: printf("1200\n"); break;
        case B1800: printf("1800\n"); break;
        case B2400: printf("2400\n"); break;
        case B4800: printf("4800\n"); break;
        case B9600: printf("9600\n"); break;
        default: printf("Fast\n"); break;
    }
}

struct flaginfo {int fl_value; char * fl_name;};

struct flaginfo input_flags[] = {
    IGNBRK, "IGnore break condition",
    BRKINT, "Signal interrupt on break",
    IGNPAR, "Ignore chars with parity errors",
    PARMRK, "Mark parity errors",
    INPCK, "Enable input parity check",
    ISTRIP, "Strip character",
    INLCR, "Map NL to CR on input",
    IGNCR, "Ignore CR",
    ICRNL, "Map CR to NL on input",
    IXON, "Enable start/top output control",
    /* _IXANY, "enable any char to restart ouput", */
    IXOFF, "Enable start/top intput control",
    0, NULL};

struct flaginfo local_flags[] = {
    ISIG, "Enable signals",
    ICANON, "Canonical input (erase and kill)",
    /* _XCASE, "Canonial upper/lower appearance", */
    ECHO, "Enable echo",
    ECHOE, "Echo ERASE as BS-SPACE-BS",
    ECHOK, "Echo KILL by starting new line",
    0, NULL};

void show_flagset(tcflag_t, struct flaginfo []);

void show_some_flags(struct termios *ttyp)
{
    show_flagset(ttyp->c_iflag, input_flags);
    show_flagset(ttyp->c_lflag, local_flags);
}

void show_flagset(tcflag_t thevalue, struct flaginfo thebitnames[])
{
    int i;
    for (i = 0; thebitnames[i].fl_value; i++){
        printf("%s is ", thebitnames[i].fl_name);

        if (thevalue & thebitnames[i].fl_value)
            printf("ON\n");
        else
            printf("OFF\n");
    }
}